
#if !defined _PACKET_PDU_H 
#define _PACKET_PDU_H

#include "RtDefines.h"
#include "RtDebug.h"
#include "RtMessageBlock.h"
#include "RtByteStream.h"

typedef BYTE			LINK_TYPE;
///connection type
enum{
	INVALID_LINK		= 0x00,
	RLB_LINK			= 0x01,				///the base packet link type
	PKG_LINK			= 0x02,				///the packet link that cache send request data
	REC_LINK			= 0x04,				///the packet link that PDU is length PDU type
	LEN_PKG_LINK		= 0x08,				///the link base packet link and support reconnect policy
	SENDER_PKG_LINK		= 0x10,				///the link base REC_LINK and make sure no drop packets during reconnect period
	ANY_LINK			= PKG_LINK | SENDER_PKG_LINK | REC_LINK | RLB_LINK			///server to fit client link type
};

typedef		BYTE		PACKET_TYPE;

enum {
	CS_PDU_TYPE_INVALID = 0,
	CS_PDU_TYPE_CONN_REQ,
	CS_PDU_TYPE_CONN_RESP,
	CS_PDU_TYPE_DISCONN,
	CS_PDU_TYPE_KEEPALIVE,
	CS_PDU_TYPE_DATA,
	CS_PDU_TYPE_RTT_REQ,
	CS_PDU_TYPE_RTT_RESP
};

///and if enable the bellow will be not compatible to old version protocol TP
//#if defined(SUPPORT_SHORT_LENGTH)
#define		PDU_LENGTH_MASK				(1 << 7) ///if the mask be set, the PDU will use short to identify the data length that length less than 64K
#define		THRESHOLD_LENGTH			((1 << 16) - 1) //the threshold of length is 2^16 - 1
//#else
//#define		PDU_LENGTH_MASK				0
//#define		THRESHOLD_LENGTH            ((1 << 32) -1)
//#endif

/*struct TBuff
{
	enum{ DEFAULT_BUFF_SIZE = 2048 };
	enum{ REDUSE_OCCUR_TIMES = 10 };
	void *m_pBuff;
	DWORD m_dwBuffSize;			//total buff size
	DWORD m_dwCursor;			//to identifier position
	BYTE m_byTimes;
	TBuff(DWORD dwInitSize = DEFAULT_BUFF_SIZE)
		:m_dwCursor(0), m_byTimes(0)
	{
		m_pBuff = malloc(dwInitSize);
		if(!m_pBuff)
			m_dwBuffSize = 0;
		else
			m_dwBuffSize = dwInitSize;
	}
	
	TBuff(const TBuff &rBuff)
	{
		free(m_pBuff);
		m_pBuff = rBuff.m_pBuff;
		m_dwBuffSize = rBuff.m_dwBuffSize;
		m_dwCursor = rBuff.m_dwCursor;
		m_byTimes = rBuff.m_byTimes;
		TBuff *pBuff  = const_cast<TBuff *>(&rBuff);
		pBuff->m_pBuff = NULL;
		pBuff->m_dwBuffSize = 0;
		pBuff->m_dwCursor = 0;
		pBuff->m_byTimes = 0;
	}

	TBuff & operator=(const TBuff &rBuff)
	{
		free(m_pBuff);
		m_pBuff = rBuff.m_pBuff;
		m_dwBuffSize = rBuff.m_dwBuffSize;
		m_dwCursor = rBuff.m_dwCursor;
		m_byTimes = rBuff.m_byTimes;
		TBuff *pBuff  = const_cast<TBuff *>(&rBuff);
		pBuff->m_pBuff = NULL;
		pBuff->m_dwBuffSize = 0;
		pBuff->m_dwCursor = 0;
		pBuff->m_byTimes = 0;
		return *this;
	}

	~TBuff()
	{
		free(m_pBuff);
	}
	RtResult resize(DWORD dwNewLen)
	{
		RT_ASSERTE(m_dwCursor <= dwNewLen);
		if(dwNewLen > m_dwBuffSize)
		{
			void *pBuff = realloc(m_pBuff, dwNewLen);
			if(pBuff)
			{
				m_pBuff = pBuff;
				m_dwBuffSize = dwNewLen;
				return RT_OK;
			}
			RT_ERROR_TRACE_THIS("TBuff::resize out of memory");
			///out of memory
			return RT_ERROR_FAILURE;
		}
		if(((dwNewLen < DEFAULT_BUFF_SIZE && m_dwBuffSize > DEFAULT_BUFF_SIZE) 
			&& (++m_byTimes) < REDUSE_OCCUR_TIMES))
			return RT_OK;

		m_byTimes = 0;
		if(dwNewLen > DEFAULT_BUFF_SIZE || m_dwBuffSize <= DEFAULT_BUFF_SIZE){
			return RT_OK;
		}
		
		dwNewLen = DEFAULT_BUFF_SIZE;
		void *pBuff = realloc(m_pBuff, dwNewLen);
		if(!pBuff)
		{
			RT_ERROR_TRACE_THIS("TBuff::resize out of memory");
			return RT_ERROR_FAILURE;
		}
		m_pBuff = pBuff;
		m_dwBuffSize = dwNewLen;
		RT_ASSERTE(m_dwBuffSize > m_dwCursor);
		return RT_OK;
	}
};*/
/*
 *	all decode will skip packet type, because of that packet type already 
 *	be read in PeekType function
 */
class CLenPacketPDU
{
public:
	CLenPacketPDU();
	~CLenPacketPDU();

	static DWORD GetFixLength(PACKET_TYPE PacketType, BOOL bNeedAck = FALSE)
	{
		return sizeof(DWORD);
	}

	static RtResult PeekType(LPVOID msg, PACKET_TYPE &type)
	{
		type = CS_PDU_TYPE_DATA;
		return RT_OK;
	}

	static RtResult PeekType(CRtMessageBlock &Block, PACKET_TYPE &type)
	{
		type = CS_PDU_TYPE_DATA;
		return RT_OK;
	}

	RtResult Encode(CRtMessageBlock& mb, CRtMessageBlock &mbData, BOOL bNeedAck, DWORD dwAck);
	RtResult DecodeWithoutData(CRtMessageBlock& mb, BOOL bNeedAck);
//	RtResult DecodeWithoutData(LPVOID msg, BOOL bNeedAck);
	RtResult DecodeData(CRtMessageBlock& mb, BOOL bNeedAck);
	CRtMessageBlock *GetData(){ return m_pmbData; }
	DWORD GetDataLen() { return m_dwDataLen; }
	DWORD GetAck()	{ return 0; }
	
private:
	DWORD	m_dwDataLen;
	CRtMessageBlock		*m_pmbData;
	//TBuff	m_DataBuff;
};

class CPacketPDU
{
public:
	CPacketPDU(BYTE byPacketType = CS_PDU_TYPE_INVALID);
	virtual ~CPacketPDU();
	static DWORD GetFixLength(PACKET_TYPE PacketType, BOOL bNeedAck = FALSE)
	{
		DWORD dwLen = sizeof(BYTE);
		switch(PacketType & (~PDU_LENGTH_MASK)) {
		case CS_PDU_TYPE_CONN_REQ:
		case CS_PDU_TYPE_CONN_RESP:
			dwLen += sizeof(WORD) + sizeof(BYTE);
			dwLen += sizeof(DWORD);
			break;
		case CS_PDU_TYPE_DISCONN:
			dwLen += sizeof(WORD);
			break;
		case CS_PDU_TYPE_KEEPALIVE:
		case CS_PDU_TYPE_RTT_REQ:
		case CS_PDU_TYPE_RTT_RESP:
			dwLen += sizeof(DWORD);			
			break;

		case CS_PDU_TYPE_DATA:
		{
			BOOL bLengthMask = PacketType & PDU_LENGTH_MASK;
			dwLen += bLengthMask ? sizeof(WORD) : sizeof(DWORD);
			if(bNeedAck)
				dwLen += sizeof(DWORD);
			break;
		}
		default:
			RT_ASSERTE_RETURN(FALSE, 0);
		}
		return dwLen;
	}
	static RtResult PeekType(CRtMessageBlock& mb, PACKET_TYPE &type)
	{
		return mb.Read(&type, sizeof(PACKET_TYPE), NULL, FALSE);
		//type = *((PACKET_TYPE *)msg);
		//return RT_OK;
	}

protected:
	BYTE	m_byPacketType;
};

class CDataPktPDU: public CPacketPDU
{
public:
	CDataPktPDU();

	~CDataPktPDU();

	RtResult DecodeWithoutData(CRtMessageBlock& mb, BOOL bNeedAck);
	//RtResult DecodeWithoutData(LPVOID msg, BOOL bNeedAck);
	RtResult DecodeData(CRtMessageBlock& mb, BOOL bNeedAck);
	RtResult Encode(CRtMessageBlock& mb, CRtMessageBlock &mbData, BOOL bNeedAck, DWORD dwAck);

	DWORD GetAck() { return m_dwACK; }
	CRtMessageBlock *GetData() { return m_pmbData; }
	DWORD GetDataLen() { return m_dwDataLen; }
	
private:
	DWORD			m_dwACK;
	DWORD			m_dwDataLen;
	BOOL				m_bNeedAck;
	CRtMessageBlock		*m_pmbData;
	//TBuff				m_DataBuff;
};

class CKDRPDU: public CPacketPDU
{
public:
	CKDRPDU(PACKET_TYPE byPktType, DWORD dwValue = 0);
	virtual ~CKDRPDU();
	RtResult Decode(CRtMessageBlock& mb);
	//RtResult Decode(TBuff &rBuff);
	RtResult Encode(CRtMessageBlock& mb);
	DWORD GetValue() { return m_dwValue; }
private:
	DWORD	m_dwValue;
};

class CReqPDU: public CPacketPDU
{
public:
	CReqPDU();
	CReqPDU(BYTE byType, WORD wTag = 0, BOOL bNeedACK = FALSE, DWORD dwAck = 0);
	RtResult Decode(CRtMessageBlock& mb);
	//RtResult Decode(TBuff &rBuff)
	RtResult Encode(CRtMessageBlock& mb);
	BYTE GetType() { return m_byType; }
	WORD GetTag() {	return m_wTag; }
	DWORD GetAck() { return m_dwACK; }

protected:
	BYTE	m_byType;
	WORD	m_wTag;
	DWORD	m_dwACK;
	BOOL	m_bNeedAck;
};

class CRespPDU: public CReqPDU{
public:
	CRespPDU(BOOL bNeedAck); //for decode
	CRespPDU(WORD wTag, BOOL bNeedAck, BYTE byLinkType, DWORD dwAck = 0); //for encode
	virtual ~CRespPDU();
};

class CDisconnectPDU: public CPacketPDU
{
public:
	CDisconnectPDU(WORD wValue = 0);
	virtual ~CDisconnectPDU();
	RtResult Decode(CRtMessageBlock& mb);
//	RtResult Decode(TBuff &rBuff);
		
	RtResult Encode(CRtMessageBlock& mb);
	WORD GetValue() { return m_wValue; }
private:
	WORD	m_wValue;
};

#endif

